﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Data.Objects.DataClasses;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using Microsoft.Practices.Prism.Events;
using SpeakToMe.Core.Models;
using SpeakToMe.Core.Enums;
using SpeakToMe.Core.Interfaces;
using SpeakToMe.Speech.Questions;
using SpeakToMe.Speech.Tokens;
using SpeakToMe.Core.Events;
using SpeakToMe.Core;


namespace SpeakToMe.Speech
{
    //Represent the context in which a command was receieved.  Allows access to previous conversation messages and optional state associated with each
    [Export]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class ConversationContext
    {
        public User ConversationUser { get; set; }
        public Conversation Conversation { get; set; }
        public List<ConversationHistory> ConversationHistory { get; set; }
        public ConversationType Mode { get; set; }
        public ISmartHomeServiceCallback Callback { get; set; }
        public string Address { get; set; }

        [Import]
        private IEventAggregator EventAggregator { get; set; }

        [Import]
        public QuestionManager QuestionManagerReference { get; set; }

        public void Init(int userId, ConversationType mode,
            string address, ISmartHomeServiceCallback callback)
        {
            IUserData userData = ServiceLocator.GetInstance<IUserData>();
            this.ConversationUser = userData.GetUserById(userId);

            this.Conversation = userData.GetConversation(userId, mode, address);
            this.ConversationHistory = userData.GetConversationHistory(this.Conversation.ID);

            this.Mode = mode;
            this.Callback = callback;
            this.Address = address;
        }

        public void LogRequest(string request, object tag)
        {
            string tagString = this.SerializeTag(tag);

            IUserData userData = ServiceLocator.GetInstance<IUserData>();
            var history = userData.AddConversationHistory(this.Conversation.ID, request, tagString, tag.GetType().ToString(), true);

            this.ConversationHistory.Add(history);
        }

        public void Say(string comment, object tag)
        {
            string tagString = string.Empty;
            tagString = this.SerializeTag(tag);

            this.EventAggregator.GetEvent<ReplyToChannelEvent>().Publish(new ReplyToChannelEventArgs
            {
                Mode = (ConversationType)this.Conversation.Mode,
                Reply = comment,
                TagString = tagString,
                Tag = tag,
                ConversationId = this.Conversation.ID,
                Callback = this.Callback,
                UserId = this.ConversationUser.ID,
                Address = this.Address
            });
        }

        private string SerializeTag(object tag)
        {
            string tagString = string.Empty;

            if (tag != null)
            {
                if (tag is IEntityWithChangeTracker)
                {
                    (tag as IEntityWithChangeTracker).SetChangeTracker(null);
                }
                var ser = new DataContractSerializer(tag.GetType());
                var ms = new MemoryStream();
                ser.WriteObject(ms, tag);
                tagString = Encoding.Default.GetString(ms.ToArray());
            }

            return tagString;
        }

        public void AskQuestion(string text, List<Token> expectedReplies, object state,
            Action<ConversationContext, object, List<Token>> executeOnAnswer)
        {
            Question question = new Question
            {
                Address = this.Address,
                ExecuteIfAnswered = executeOnAnswer,
                ExpectedReplys = expectedReplies,
                Mode = this.Mode,
                PosedDateTime = DateTime.Now,
                UserId = this.ConversationUser.ID,
                State = state,
                QuestionText = text
            };

            this.QuestionManagerReference.AddQuestion(question);

            this.Say(text, null);
        }
    }
}